#ifndef PORTABLE_BYTECODE_DECODER_H_
#define PORTABLE_BYTECODE_DECODER_H_

#include <iosfwd>

#include "src/pbc/portable-bytecode-register.h"

namespace portable {

namespace {
template <typename T>
inline T ReadUnalignedValue(Address p) {
  T r;
  memcpy(&r, reinterpret_cast<void*>(p), sizeof(T));
  return r;
}
}  // namespace

class BytecodeDecoder final {
 public:
  // Decodes a register operand in a byte array.
  static Register DecodeRegisterOperand(Address operand_start,
                                        OperandType operand_type,
                                        OperandScale operand_scale) {
    P_DCHECK(Bytecodes::IsRegisterOperandType(operand_type));
    int32_t operand =
        DecodeSignedOperand(operand_start, operand_type, operand_scale);
    return Register::FromOperand(operand);
  }

  // Decodes a register list operand in a byte array.
  static RegisterList DecodeRegisterListOperand(Address operand_start,
                                                uint32_t count,
                                                OperandType operand_type,
                                                OperandScale operand_scale) {
    Register first_reg =
        DecodeRegisterOperand(operand_start, operand_type, operand_scale);
    return RegisterList(first_reg.index(), static_cast<int>(count));
  }

  // Decodes a signed operand in a byte array.
  static int32_t DecodeSignedOperand(Address operand_start,
                                     OperandType operand_type,
                                     OperandScale operand_scale) {
    P_DCHECK(!Bytecodes::IsUnsignedOperandType(operand_type));
    switch (Bytecodes::SizeOfOperand(operand_type, operand_scale)) {
      case OperandSize::kByte:
        return *reinterpret_cast<const int8_t*>(operand_start);
      case OperandSize::kShort:
        return static_cast<int16_t>(
            ReadUnalignedValue<uint16_t>(operand_start));
      case OperandSize::kQuad:
        return static_cast<int32_t>(
            ReadUnalignedValue<uint32_t>(operand_start));
      case OperandSize::kNone:
        P_UNREACHABLE();
    }
    return 0;
  }

  // Decodes an unsigned operand in a byte array.
  static uint32_t DecodeUnsignedOperand(Address operand_start,
                                        OperandType operand_type,
                                        OperandScale operand_scale) {
    P_DCHECK(Bytecodes::IsUnsignedOperandType(operand_type));
    switch (Bytecodes::SizeOfOperand(operand_type, operand_scale)) {
      case OperandSize::kByte:
        return *reinterpret_cast<const uint8_t*>(operand_start);
      case OperandSize::kShort:
        return ReadUnalignedValue<uint16_t>(operand_start);
      case OperandSize::kQuad:
        return ReadUnalignedValue<uint32_t>(operand_start);
      case OperandSize::kNone:
        P_UNREACHABLE();
    }
    return 0;
  }

  // Decode a single bytecode and operands to |os|.
  static std::ostream& Decode(std::ostream& os, const uint8_t* bytecode_start,
                              bool with_hex = true);
};

}  // namespace portable

#endif  // PORTABLE_BYTECODE_DECODER_H_
